htmlwidgets는 자바스크립트를 R에 임베딩하는 프레임워크이며, htmlwidgets를 통해 만들어진 결과물들은 interactivity를 제외하면 다른 R 플롯과 동일하게 작동합니다. 수많은 R 패키지들이 htmlwidgets를 통해 개발되었고, htmlwidgets 갤러리를 방문하시면 그 목록들을 확인해볼 수 있습니다. 물론 이 글에서 이 패키지들을 전부 다룰 수는 없으며, 대표적인 몇 가지 패키지와 애드온만을 가지고 실습을 진행할 것입니다.
이번 장에서 활용할 데이터는 코에이의 시뮬레이션 게임 삼국지13의 도시별 데이터와 무장 데이터입니다. 먼저 도시별 데이터를 읽어온 후, 필요한 컬럼들을 선택하고 인물에 따라 위, 촉, 오를 구분하였습니다. 위, 촉, 오에 속하지 않는 도시들은 걸러내고 비병역인구와 병역인구를 합쳐 인구 컬럼을 생성하였습니다. 최종적으로 만들어진 데이터는 다음과 같습니다. 데이터는 여기에서 다운로드하시면 됩니다.
library(tidyverse)
library(readxl)
# 데이터 읽기
city = read_excel("data/kingdoms.xlsx", sheet='도시')
# 전처리
city = city %>%
select(지방, 도시, 주, 주도, 군단, 태수, 자금, 군량, 비병역인구, 병역인구, 상업, 농업, 문화) %>%
# 군단에 따라 위, 촉, 오로 구분
mutate(삼국 = case_when(군단 == "관우" ~ "촉",
군단 == "유비" ~ "촉",
군단 == "장로" ~ "촉",
군단 == "조조" ~ "위",
군단 == "장료" ~ "위",
군단 == "공손공" ~ "위",
군단 == "손권" ~ "오")) %>%
# 위촉오에 속하지 않는 도시 걸러냄
filter(!is.na(삼국)) %>%
# 인구 컬럼 생성
mutate(인구 = 비병역인구 + 병역인구)
head(city)
## # A tibble: 6 x 15
## 지방 도시 주 주도 군단 태수 자금 군량 비병역인구 병역인구 상업
## <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 하북 양평 유주 × 공손공~ 공손공~ 3000 30800 138200 15400 2320
## 2 하북 북평 유주 × 조조 조창 3600 30000 135200 15000 2340
## 3 하북 계 유주 ○ 조조 전예 4150 49000 220500 24500 2780
## 4 하북 남피 기주 × 조조 양습 3950 47600 214200 23800 2640
## 5 하북 중산 기주 × 조조 진림 3850 42400 190800 21200 2270
## 6 하북 거록 기주 × 조조 장패 4250 51200 228600 25600 2340
## # ... with 4 more variables: 농업 <dbl>, 문화 <dbl>, 삼국 <chr>, 인구 <dbl>
DT 패키지를 통해서 문서 안에 손쉽게 인터랙티브 테이블을 포함할 수 있습니다. DT::datatable()을 통해 생성한 결과가 head(data)로 출력한 결과보다 훨씬 알아보기 쉽습니다. DT 패키지를 통해 생성된 인터랙티브 테이블은 페이지 넘김, 정렬, 검색 등의 기능을 제공합니다. 따라서 문서의 독자는 표로부터 원하는 정보를 보다 쉽게 얻어갈 수 있습니다.
city %>% DT::datatable()
ggplot2이번에는 인터랙티브 플롯을 다뤄볼 차례입니다. 많은 R 사용자들에게 익숙한 ggplot2 패키지에 plotly 패키지를 조합하여 손쉽게 인터랙티브 플롯을 그릴 수 있습니다.
왼쪽 플롯은 ggplot패키지를 이용해 그린 산점도를 인터랙티브 플롯으로 바꿔준 예제입니다. 도시별 데이터에서 산점도 객체를 생성한 후, 이 객체를 ggplotly 함수에 넣어줍니다. 이제 점 위에 커서를 가져다 대면 국가, 자금, 군량에 대한 툴팁이 표시됩니다. 플롯의 툴팁은 기본적으로 aes 에 전달된 모든 정보를 포함합니다.
오른쪽 플롯은 툴팁을 직접 수정해준 예제입니다. ggplot의 aes에 text 파라미터를 추가하고, 원하는 툴팁 내용을 문자열로 입력해주면 됩니다. 이번 예제에서는 인구 정보를 추가하였습니다. 마지막으로 ggplotly함수에 tooptip='text'를 넣어주면 끝입니다.
library(plotly)
font = list(
family = "나눔스퀘어라운드 Regular",
size = 14,
color = "black")
subtitle = list(
text = "1) 인터랙티브 플롯 예제",
font=font,
xref = "paper",
yref = "paper",
yanchor = "bottom",
xanchor = "center",
align = "left",
x = 0.5,
y = 1,
showarrow = FALSE
)
p = city %>%
# x축에 자금, y축에 군량, 국가별로 마커 색과 모양 구분
ggplot(aes(x=자금, y=군량, color=삼국, shape=삼국)) +
geom_point() + # 산점도
theme_bw()
p1 = ggplotly(p) %>% hide_legend() %>% layout(annotations=subtitle) # 인터랙티브 플롯으로
p = city %>%
ggplot(aes(x=자금,
y=군량,
color=삼국,
shape=삼국,
# 툴팁 텍스트 수정
text=paste0("국가: ", 삼국, "\n",
"자금: ", 자금, "\n",
"군량: ", 군량, "\n",
"인구: ", 인구))) +
geom_point() +
theme_bw()
# 툴팁에 text만 표시되도록 tooptip='text' 전달
subtitle['text'] = "2) 툴팁 수정 예제"
p2 = ggplotly(p, tooltip='text') %>% layout(annotations=subtitle)
plotly::subplot(p1, p2, margin = 0.025, shareY=T, shareX=T) %>%
layout(autosize = F, width = 800, height = 350)
이번에는 하이라이트 기능을 통해서 원하는 데이터 포인트를 강조해보도록 하겠습니다. 데이터로부터 ggplot 객체를 생성하기 전에, highlight_key 함수를 통해서 하이라이트의 키가 될 변수를 설정해줍니다. 이후에는 똑같이 ggplotly 객체를 생성해주면 됩니다. 마지막으로 ggplotly 객체를 highlight 함수에 넣어서 하이라이트 플롯을 생성합니다. highlight 함수의 파라미터에 대한 더 구체적인 설명은 여기를 참고해주세요.
# 하이라이트 키가 될 변수를 지정해줍니다.
kingdoms_highlight = highlight_key(city, ~삼국)
# ggplot 객체를 생성합니다.
p = kingdoms_highlight %>%
ggplot(aes(x=자금,
y=군량,
color=삼국,
shape=삼국,
text=paste0("국가: ", 삼국, "\n",
"자금: ", 자금, "\n",
"군량: ", 군량, "\n",
"인구: ", 인구))) +
geom_point() +
theme_bw() +
ggtitle("3) 하이라이트를 적용한 인터랙티브 플롯 예제")
# ggplotly 객체를 생성하고 highlight_gg에 할당합니다.
highlight_gg = ggplotly(p, tooltip = "text")
# 하이라이트 플롯을 생성합니다.
highlight(highlight_gg,
# 클릭을 통해서 하이라이트가 활성화되도록 합니다.
on = "plotly_click",
# 선택된 키들을 표시합니다.
selectize = TRUE)
위에서 살펴본 기법들을 다른 형식의 플롯들에도 적용해보겠습니다. 일단 추가적으로 사용할 삼국지 무장 데이터를 준비해줍니다. 도시를 통해서 무장의 소속 국가를 매칭하고, 위촉오 삼국에 속하지 않는 무장들을 걸러냈습니다. 완성된 데이터는 다음과 같습니다.
general = read_excel("data/kingdoms.xlsx", sheet="무장")
# city 데이터의 '도시'와 '삼국' 컬럼을 뽑아내줍니다.
mapping = city %>% select(소재=도시, 삼국)
general = general %>%
select(무장, 군단, 소속, 소재, 성별, 통솔=원시통솔, 무력=원시무력, 지력=원시지력, 정치=원시정치, 생년, 등장년, 졸년, 혈연) %>%
left_join(mapping) %>%
filter(!is.na(삼국)) %>%
mutate(삼국 = factor(삼국, levels=c("위", "오", "촉")))
general %>%
select(삼국, everything()) %>%
DT::datatable()
다음은 각 무장의 주요 능력을 통해 성향을 분류하고, 국가별로 무장의 성향 비율을 표현한 예제입니다. 산점도가 아닌 막대 그래프를 그렸다는 점을 제외하면 앞의 예제들과 거의 같습니다. 먼저 which.max 함수를 통해서 장수의 가장 뛰어난 능력이 무엇인지를 파악하고, 이 결과를 general 데이터에 추가해줍니다.
# 각 무장의 성향을 분류합니다
chartype = c("통솔","무력","지력","정치")[apply(general %>% select(통솔, 무력, 지력, 정치), 1, which.max)]
# general 데이터프레임에 성향을 붙여줍니다
general = general %>% mutate(성향 = chartype)
# 국가별로 각 성향의 무장 비율을 계산합니다
main = general %>%
group_by(삼국, 성향) %>%
summarise(count = n()) %>%
group_by(삼국) %>%
mutate(total = sum(count)) %>%
mutate(비율 = count/total)
국가별/성향별 장수 비율을 계산하고, 하이라이트 키를 지정해줍니다 하이라이트 키를 지정한 데이터로 막대 그래프를 그리고 facet_wrap으로 국가를 분리해줍니다. 이를 인터랙티브 플롯으로 만들어 주면 끝입니다.
# 하이라이트 키를 성향으로 지정해줍니다.
main_highlight = highlight_key(main, ~성향)
# 막대 그래프를 그린 후 facet_wrap 함수를 통해 국가별로 분리합니다.
p = main_highlight %>%
ggplot(aes(x=성향,
y=비율,
fill = 성향,
text=paste0("국가: ", 삼국, "\n",
"성향: ", 성향, "\n",
"비율: ", round(비율,2)))) +
geom_bar(stat='identity') +
facet_wrap(삼국 ~.) +
theme_bw()
# 인터랙티브 플롯을 생성합니다.
highlight_gg = ggplotly(p, tooltip = "text")
highlight(highlight_gg, on = "plotly_click", selectize = F)
이번에는 각 국가별, 연도별 무장의 수를 선 그래프로 나타내보겠습니다. 먼저 무장의 등장년과 졸년을 기준으로 해당 무장이 활동중인지 여부를 구하고, 연도별/국가별로 더해줍니다. 이후 그래프를 그리기 적절하도록 데이터프레임을 조작해줍니다.
start = min(general$등장년)
end = max(general$졸년)
serve = (start:end) %>%
lapply(function(x){return(as.integer(general$등장년<=x & general$졸년>x))}) %>%
data.frame() %>%
mutate(무장 = general$무장) %>%
left_join(general %>% select(삼국, 무장)) %>%
group_by(삼국) %>%
summarise_if(is.numeric, sum) %>%
column_to_rownames("삼국") %>%
t() %>%
data.frame() %>%
stack(c(위,촉,오)) %>%
mutate(연도 = rep(start:end,3)) %>%
setNames(c("무장수", "국가", "연도"))
이제 만들어진 데이터를 가지고 똑같이 플롯을 그려주면 끝입니다. 코드의 흐름은 역시 지금까지와 같습니다.
# 하이라이트 키를 지정해줍니다.
serve_highlight = highlight_key(serve, ~국가)
# 선 그래프를 그려줍니다
p = serve_highlight %>%
ggplot(aes(x=연도,
y=무장수,
group=국가,
color=국가,
text = paste0("연도: ", 연도, "\n",
"국가: ", 국가, "\n",
"무장 수: ", 무장수, "\n"))) +
geom_line(size=0.5) +
theme_bw()
# 인터랙티브 플롯을 생성합니다.
highlight_gg = ggplotly(p, tooltip = "text")
highlight(highlight_gg, on = "plotly_click", selectize = F) %>%
layout(autosize = F, width = 800, height = 350)
이번에는 간단한 애니메이션을 적용한 플롯을 다뤄보겠습니다. gganimate 패키지를 활용하여 ggplot 객체에 애니메이션을 적용하고, gif로 렌더링할 것입니다. 다음 패키지들을 먼저 설치해주세요.
위에서 만들어둔 serve(연도별 무장 수) 데이터는 시계열적 특성을 가지고 있기 때문에 애니메이션을 적용하기 좋습니다. 그래프를 그릴 때 사용하눈 문법은 일반적인 ggplot 문법과 같으며, transition_reveal에 애니메이션의 시간축을 설정해주면 됩니다. 이 결과를 animate 함수에 넣고 gif 렌더링을 위해 renderer = gifski_renderer()로 지정해주면 끝입니다. 너비와 높이는 원하는대로 지정해주시면 되고, anim_save함수를 통해서 결과를 저장하는 것도 가능합니다.
library(gganimate)
p = serve %>%
ggplot(aes(x=연도,
y=무장수,
text = paste0("연도 :", 연도, "\n",
"국가 :", 국가, "\n",
"무장 수", 무장수, "\n"))) +
geom_line(aes(group=국가,color=국가),size=1) +
transition_reveal(연도) +
theme_bw()
animate(p, renderer = gifski_renderer(), width=750, height=250)
anim_save("assets/fig/anim1.gif")